package org.python.pydev.debug.newconsole;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.model.PyStackFrame;
import org.python.pydev.debug.model.XMLUtils;
import com.aptana.interactive_console.console.IScriptConsoleCommunication;
import com.aptana.interactive_console.console.InterpreterResponse;
import com.aptana.shared_core.callbacks.ICallback;
import com.aptana.shared_core.structure.Tuple;
/**
* This class allows console to communicate with python backend by using the existing
* debug connection.
*
* @author hussain.bohra
* @author Fabio Zadrozny
*/
public class PydevDebugConsoleCommunication implements IScriptConsoleCommunication {
private int TIMEOUT = PydevConsoleConstants.CONSOLE_TIMEOUT;
String EMPTY = (String) StringUtils.EMPTY;
/**
* Signals that the next command added should be sent as an input to the server.
*/
private volatile boolean waitingForInput;
/**
* Input that should be sent to the server (waiting for raw_input)
*/
private volatile String inputReceived;
/**
* Helper to keep on busy loop.
*/
private volatile Object lock = new Object();
/**
* Response that should be sent back to the shell.
*/
private volatile InterpreterResponse nextResponse;
private final PydevDebugConsoleFrame consoleFrame;
public PydevDebugConsoleCommunication() {
consoleFrame = new PydevDebugConsoleFrame();
}
public void execInterpreter(final String command, final ICallback<Object, InterpreterResponse> onResponseReceived,
final ICallback<Object, Tuple<String, String>> onContentsReceived) {
nextResponse = null;
if (waitingForInput) {
inputReceived = command;
waitingForInput = false;
// the thread that we started in the last exec is still alive if we were waiting for an input.
} else {
// create a thread that'll keep locked until an answer is received from the server.
Job job = new Job("PyDev Debug Console Communication") {
@Override
protected IStatus run(IProgressMonitor monitor) {
PyStackFrame frame = consoleFrame.getLastSelectedFrame();
if (frame == null) {
nextResponse = new InterpreterResponse(EMPTY,
"[Invalid Frame]: Please select frame to connect the console." + "\n", false, false);
return Status.CANCEL_STATUS;
}
final EvaluateDebugConsoleExpression evaluateDebugConsoleExpression = new EvaluateDebugConsoleExpression(
frame);
evaluateDebugConsoleExpression.executeCommand(command);
String result = evaluateDebugConsoleExpression.waitForCommand();
try {
if (result.length() == 0) {
//timed out
nextResponse = new InterpreterResponse(result, EMPTY, false, false);
return Status.CANCEL_STATUS;
} else {
EvaluateDebugConsoleExpression.PydevDebugConsoleMessage consoleMessage = XMLUtils
.getConsoleMessage(result);
nextResponse = new InterpreterResponse(consoleMessage.getOutputMessage().toString(),
consoleMessage.getErrorMessage().toString(), consoleMessage.isMore(), false);
}
} catch (CoreException e) {
Log.log(e);
nextResponse = new InterpreterResponse(result, EMPTY, false, false);
return Status.CANCEL_STATUS;
}
return Status.OK_STATUS;
}
};
job.schedule();
}
int timeOut = TIMEOUT; //only get contents each 500 millis...
// busy loop until we have a response
while (nextResponse == null) {
synchronized (lock) {
try {
lock.wait(20);
} catch (InterruptedException e) {
}
}
timeOut -= 20;
if (timeOut <= 0 && nextResponse == null) {
timeOut = TIMEOUT / 2; // after the first, get it each 250 millis
}
}
onResponseReceived.call(nextResponse);
}
public ICompletionProposal[] getCompletions(String text, String actTok, int offset) throws Exception {
ICompletionProposal[] receivedCompletions = {};
if (waitingForInput) {
return new ICompletionProposal[0];
}
PyStackFrame frame = consoleFrame.getLastSelectedFrame();
if (frame == null) {
return new ICompletionProposal[0];
}
final EvaluateDebugConsoleExpression evaluateDebugConsoleExpression = new EvaluateDebugConsoleExpression(frame);
String result = evaluateDebugConsoleExpression.getCompletions(actTok, offset);
if (result.length() > 0) {
List<Object[]> fromServer = XMLUtils.convertXMLcompletionsFromConsole(result);
List<ICompletionProposal> ret = new ArrayList<ICompletionProposal>();
PydevConsoleCommunication.convertToICompletions(text, actTok, offset, fromServer, ret);
receivedCompletions = ret.toArray(new ICompletionProposal[ret.size()]);
}
return receivedCompletions;
}
public String getDescription(String text) throws Exception {
return null;
}
/**
* Enable/Disable linking of the debug console with the suspended frame.
*/
public void linkWithDebugSelection(boolean isLinkedWithDebug) {
consoleFrame.linkWithDebugSelection(isLinkedWithDebug);
}
public void close() throws Exception {
//Do nothing on console close.
}
}